MCP · Agent safety

QueryAPIGate against the Altinity checklist, point by point

Altinity's post Is It Safe Letting AI Agents Touch Production Data (Without the Tears) argues that prompts ("never drop tables") are suggestions, so the limits that keep an agent safe have to live in the infrastructure, where the agent can't override them. Below is every control it recommends, what QueryAPIGate does, and a one-line explanation of each.

19recommendations
16covered
1partial
2out of scope
Verdict

QueryAPIGate covers about 80% of the checklist: essentially the whole data-and-identity layer. The remaining 20% is one separate layer: hardening the machine the agent runs on.

Legend:Covereda control the gateway enforcesPartialthe data path onlyOut of scopea different layer
The checklist

Nineteen recommendations, one line each

The article's recommendation on the left, what QueryAPIGate does on the right. Each row is explained below.

#Article recommendationQueryAPIGateStatus
01Prompts aren't a boundary; enforce in infrastructure the agent can't overrideGateway enforces grants and the SQL guard; the agent never gets a database connectionCovered
02A dedicated read-only role for agents, not shared or admin accountsScoped API keys and roles, read-only by defaultCovered
03SELECT only on specific databases and tables; least privilegeallowed_tables plus per-connection scopingCovered
04A maximum execution time, resource and thread caps, read-only sessionsPer-key query_timeout, max_rows, max_concurrent; read-only through the SQL guardCovered
05Quotas: query counts per time windowrate_limit plus usage quotas (calls and rows per day or month)Covered
06Row policies: restrict rows by identity (one region, say)JWT claim binding: a saved query takes a parameter from the user's tokenCovered
07OAuth/OIDC from a central identity provider instead of per-agent passwordsJWT sign-in, OAuth for the MCP server, SSO for the ConsoleCovered
08Short-lived tokens; central grant and revoke; rotated credentialsJWT expiry; API-key rotation and expiry; claim → role and workspace mappingCovered
09An MCP server as the enforcement gateway between the LLM and the databaseThis is what QueryAPIGate is (ADR 0006)Covered
10The client authenticates itself; the token authorizes the gateway, not the databaseDatabase credentials held by the gateway, never exposed to the LLMCovered
11Restrict MCP to read-onlyMCP execute_sql is forced read-only regardless of the key's write grantCovered
12Permissions must cover schema and metadata, not just rowsdescribe_table and the schema are masking-aware; masked columns are flagged, not shownCovered
13Results depend entirely on the identity behind the tokenThe key or JWT identity determines everythingCovered
14Audit everythingHash-chained audit log (ADR 0012) plus run history, streamable to a SIEMCovered
15A human in the loop for risky changesAgent writes need approval (ADR 0008), then run with the exact argumentsCovered
16Pre-registered OAuth callback URLs onlyResource-server model: callbacks are registered at your IdP; the token's aud is bound to the MCP URLCovered
17Sandbox the agent's host: no kubectl, aws-cli or local database clientsGoverns the data path, not the agent's machineOut of scope
18Don't mount personal credentials; inventory the other sharp objectsA property of the host, not the gatewayOut of scope
19Agents write infrastructure tools (Terraform, bash) for humans to runThe approval flow covers data writes; infrastructure tooling is broaderPartial
Point by point

What each control means in practice

Where a point rests on a design decision, the ADR is the project's own record of it.

  1. 01Covered

    Enforce in infrastructure, not the prompt

    The agent only ever talks to QueryAPIGate, never to the database: it has no connection to override. Every request is checked by the gateway's grants and SQL guard, so a prompt that says "drop the table" has nothing to act on.

  2. 02Covered

    A dedicated read-only role

    You mint scoped API keys (or roles that keys inherit) for agents instead of handing over an admin or personal account. Every key is read-only unless you explicitly grant writes, so read-only is the default rather than a reminder.

  3. 03Covered

    SELECT on specific tables only

    allowed_tables restricts a key to a named set of tables, enforced by parsing the SQL, not by trusting the query. A key also only reaches the connections it has been granted, so least privilege applies at both the connection and the table level.

  4. 04Covered

    Caps on time and resources

    Each key can carry a query timeout, a maximum rows per call and a maximum number of concurrent queries; these only narrow the server's own limits, never widen them. (Altinity's thread-per-query cap is a ClickHouse setting the database itself enforces.)

  5. 05Covered

    Quotas against floods

    A key gets a rate limit (100/minute, say) plus optional usage quotas that count calls and rows per day or month, so a runaway agent can't bury the database in requests.

  6. 06Covered

    Row policies by identity

    A JWT-bound user reaches only saved queries that take a parameter from their verified token claim (from_claim), so the "which rows" filter is signed into the request, not chosen by the agent. It is claim-bound parameterisation rather than arbitrary SQL row policies, and it covers the "see only your region" case cleanly.

  7. 07Covered

    OAuth/OIDC from a central identity provider

    Users sign in with a JWT from any OIDC provider (Keycloak, Okta, Entra), the MCP server accepts OAuth tokens through standard discovery, and the Console supports SSO. Identity comes from the identity provider, not a per-agent password.

  8. 08Covered

    Short-lived, centrally managed, rotated

    JWTs expire on their own; API keys rotate (the old secret keeps working through a grace window) and can carry an expiry date; a token's claims map to a role and workspace, so you grant and revoke centrally instead of editing a password list.

  9. 09Covered

    The MCP server as the gateway

    This is precisely what QueryAPIGate is (ADR 0006): it sits between the agent and the database and exposes only governed saved queries and a read-only ad-hoc tool.

  10. 10Covered

    The token authorizes the gateway, not the database

    Each client presents its own key or bearer token, which authorizes the gateway only. The database credentials live inside the gateway, encrypted at rest, and are never sent to the LLM, so the model can't reuse them directly.

  11. 11Covered

    MCP forced read-only

    The ad-hoc MCP SQL tool is read-only no matter what the key is otherwise allowed to do: a deliberately strict default for agent traffic.

  12. 12Covered

    Metadata is governed too

    Agents probe the schema to find data, so describe_table and schema browsing are masking-aware: a masked or dropped column is flagged, never shown as readable. An agent can't even learn that a hidden column holds real values.

  13. 13Covered

    Identity decides the result

    Every grant, mask, limit and row filter resolves from the authenticated key or user, so the same query returns different results, or an access denied, depending on who is asking.

  14. 14Covered

    Audit everything

    Every action is recorded in a hash-chained audit log: each entry is hashed over the previous one, so changing, removing or reordering a record is detectable (ADR 0012). Runs are kept as history, and the log can stream to a SIEM. The article under-emphasises this; it is a QueryAPIGate strength.

  15. 15Covered

    A human in the loop for writes

    When an agent asks to write over MCP, the request is held for a person to approve and then runs exactly once, with exactly the arguments shown (ADR 0008): the human checkpoint the article calls the safest pattern.

  16. 16Covered

    Pre-registered OAuth callbacks

    QueryAPIGate is the OAuth resource server (RFC 9728), so callback-URL registration lives at your identity provider, where the spec places it. The token's audience (aud) is bound to the MCP server's own URL, so a token issued for REST, or anything else, is refused, and it can't be replayed against the database.

  17. 17Out of scope

    Sandbox the host

    Out of scope. QueryAPIGate governs the data path; it doesn't control the container or machine the agent runs on.

  18. 18Out of scope

    No personal credentials on the box; inventory sharp objects

    Out of scope, and complementary: the gateway removes one sharp object (direct database access), but hardening the agent's process and host is a separate job.

  19. 19Partial

    Agents write infrastructure tools for humans to run

    Partial. QueryAPIGate builds the approval checkpoint for data writes, but an agent authoring and running infrastructure tooling (Terraform, bash) is broader than what a data-access gateway does.

Beyond ClickHouse

Altinity's examples target ClickHouse; QueryAPIGate applies the same controls across MySQL, PostgreSQL, ClickHouse, DuckDB, MongoDB, H2, JDBC and Trino. Coverage was assessed against the QueryAPIGate codebase at v0.40.0; ADR references are the project's own.

Where to go next

The MCP server itself is on the MCP page; the standards behind these controls on Standards and specifications; the reasoning in the threat model. To try it: guide 24.